/*
 * Copyright (c) 2014, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef WEBSOCKETPP_FRAME_HPP
#define WEBSOCKETPP_FRAME_HPP

#include <algorithm>
#include <string>

#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/network.hpp>

#include <websocketpp/utilities.hpp>

namespace websocketpp
{
/// Data structures and utility functions for manipulating WebSocket frames
/**
 * namespace frame provides a number of data structures and utility functions
 * for reading, writing, and manipulating binary encoded WebSocket frames.
 */
namespace frame
{

/// Minimum length of a WebSocket frame header.
static unsigned int const BASIC_HEADER_LENGTH = 2;
/// Maximum length of a WebSocket header
static unsigned int const MAX_HEADER_LENGTH = 14;
/// Maximum length of the variable portion of the WebSocket header
static unsigned int const MAX_EXTENDED_HEADER_LENGTH = 12;

/// Two byte conversion union
union uint16_converter {
  uint16_t i;
  uint8_t  c[2];
};

/// Four byte conversion union
union uint32_converter {
  uint32_t i;
  uint8_t c[4];
};

/// Eight byte conversion union
union uint64_converter {
  uint64_t i;
  uint8_t  c[8];
};

/// Constants and utility functions related to WebSocket opcodes
/**
 * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
 */
namespace opcode
{
enum value {
  continuation = 0x0,
  text = 0x1,
  binary = 0x2,
  rsv3 = 0x3,
  rsv4 = 0x4,
  rsv5 = 0x5,
  rsv6 = 0x6,
  rsv7 = 0x7,
  close = 0x8,
  ping = 0x9,
  pong = 0xA,
  control_rsvb = 0xB,
  control_rsvc = 0xC,
  control_rsvd = 0xD,
  control_rsve = 0xE,
  control_rsvf = 0xF,

  CONTINUATION = 0x0,
  TEXT = 0x1,
  BINARY = 0x2,
  RSV3 = 0x3,
  RSV4 = 0x4,
  RSV5 = 0x5,
  RSV6 = 0x6,
  RSV7 = 0x7,
  CLOSE = 0x8,
  PING = 0x9,
  PONG = 0xA,
  CONTROL_RSVB = 0xB,
  CONTROL_RSVC = 0xC,
  CONTROL_RSVD = 0xD,
  CONTROL_RSVE = 0xE,
  CONTROL_RSVF = 0xF
};

/// Check if an opcode is reserved
/**
 * @param v The opcode to test.
 * @return Whether or not the opcode is reserved.
 */
inline bool reserved (value v)
{
  return (v >= rsv3 && v <= rsv7) ||
         (v >= control_rsvb && v <= control_rsvf);
}

/// Check if an opcode is invalid
/**
 * Invalid opcodes are negative or require greater than 4 bits to store.
 *
 * @param v The opcode to test.
 * @return Whether or not the opcode is invalid.
 */
inline bool invalid (value v)
{
  return (v > 0xF || v < 0);
}

/// Check if an opcode is for a control frame
/**
 * @param v The opcode to test.
 * @return Whether or not the opcode is a control opcode.
 */
inline bool is_control (value v)
{
  return v >= 0x8;
}
}

/// Constants related to frame and payload limits
namespace limits
{
/// Minimum length of a WebSocket frame header.
static unsigned int const basic_header_length = 2;

/// Maximum length of a WebSocket header
static unsigned int const max_header_length = 14;

/// Maximum length of the variable portion of the WebSocket header
static unsigned int const max_extended_header_length = 12;

/// Maximum size of a basic WebSocket payload
static uint8_t const payload_size_basic = 125;

/// Maximum size of an extended WebSocket payload (basic payload = 126)
static uint16_t const payload_size_extended = 0xFFFF; // 2^16, 65535

/// Maximum size of a jumbo WebSocket payload (basic payload = 127)
static uint64_t const payload_size_jumbo = 0x7FFFFFFFFFFFFFFFLL;//2^63

/// Maximum size of close frame reason
/**
 * This is payload_size_basic - 2 bytes (as first two bytes are used for
 * the close code
 */
static uint8_t const close_reason_size = 123;
}


// masks for fields in the basic header
static uint8_t const BHB0_OPCODE = 0x0F;
static uint8_t const BHB0_RSV3 = 0x10;
static uint8_t const BHB0_RSV2 = 0x20;
static uint8_t const BHB0_RSV1 = 0x40;
static uint8_t const BHB0_FIN = 0x80;

static uint8_t const BHB1_PAYLOAD = 0x7F;
static uint8_t const BHB1_MASK = 0x80;

static uint8_t const payload_size_code_16bit = 0x7E; // 126
static uint8_t const payload_size_code_64bit = 0x7F; // 127

typedef uint32_converter masking_key_type;

/// The constant size component of a WebSocket frame header
struct basic_header {
  basic_header() : b0 (0x00), b1 (0x00) {}

  basic_header (uint8_t p0, uint8_t p1) : b0 (p0), b1 (p1) {}

  basic_header (opcode::value op, uint64_t size, bool fin, bool mask,
                bool rsv1 = false, bool rsv2 = false, bool rsv3 = false) : b0 (0x00),
    b1 (0x00)
  {
    if (fin) {
      b0 |= BHB0_FIN;
    }

    if (rsv1) {
      b0 |= BHB0_RSV1;
    }

    if (rsv2) {
      b0 |= BHB0_RSV2;
    }

    if (rsv3) {
      b0 |= BHB0_RSV3;
    }

    b0 |= (op & BHB0_OPCODE);

    if (mask) {
      b1 |= BHB1_MASK;
    }

    uint8_t basic_value;

    if (size <= limits::payload_size_basic) {
      basic_value = static_cast<uint8_t> (size);
    } else if (size <= limits::payload_size_extended) {
      basic_value = payload_size_code_16bit;
    } else {
      basic_value = payload_size_code_64bit;
    }


    b1 |= basic_value;
  }

  uint8_t b0;
  uint8_t b1;
};

/// The variable size component of a WebSocket frame header
struct extended_header {
  extended_header()
  {
    std::fill_n (this->bytes, MAX_EXTENDED_HEADER_LENGTH, 0x00);
  }

  extended_header (uint64_t payload_size)
  {
    std::fill_n (this->bytes, MAX_EXTENDED_HEADER_LENGTH, 0x00);

    copy_payload (payload_size);
  }

  extended_header (uint64_t payload_size, uint32_t masking_key)
  {
    std::fill_n (this->bytes, MAX_EXTENDED_HEADER_LENGTH, 0x00);

    // Copy payload size
    int offset = copy_payload (payload_size);

    // Copy Masking Key
    uint32_converter temp32;
    temp32.i = masking_key;
    std::copy (temp32.c, temp32.c + 4, bytes + offset);
  }

  uint8_t bytes[MAX_EXTENDED_HEADER_LENGTH];
private:
  int copy_payload (uint64_t payload_size)
  {
    int payload_offset = 0;

    if (payload_size <= limits::payload_size_basic) {
      payload_offset = 8;
    } else if (payload_size <= limits::payload_size_extended) {
      payload_offset = 6;
    }

    uint64_converter temp64;
    temp64.i = lib::net::_htonll (payload_size);
    std::copy (temp64.c + payload_offset, temp64.c + 8, bytes);

    return 8 - payload_offset;
  }
};

bool get_fin (basic_header const &h);
void set_fin (basic_header &h, bool value);
bool get_rsv1 (basic_header const &h);
void set_rsv1 (basic_header &h, bool value);
bool get_rsv2 (basic_header const &h);
void set_rsv2 (basic_header &h, bool value);
bool get_rsv3 (basic_header const &h);
void set_rsv3 (basic_header &h, bool value);
opcode::value get_opcode (basic_header const &h);
bool get_masked (basic_header const &h);
void set_masked (basic_header &h, bool value);
uint8_t get_basic_size (basic_header const &);
size_t get_header_len (basic_header const &);
unsigned int get_masking_key_offset (basic_header const &);

std::string write_header (basic_header const &, extended_header const &);
masking_key_type get_masking_key (basic_header const &,
                                  extended_header const &);
uint16_t get_extended_size (extended_header const &);
uint64_t get_jumbo_size (extended_header const &);
uint64_t get_payload_size (basic_header const &, extended_header const &);

size_t prepare_masking_key (masking_key_type const &key);
size_t circshift_prepared_key (size_t prepared_key, size_t offset);

// Functions for performing xor based masking and unmasking
template <typename input_iter, typename output_iter>
void byte_mask (input_iter b, input_iter e, output_iter o, masking_key_type
                const &key, size_t key_offset = 0);
template <typename iter_type>
void byte_mask (iter_type b, iter_type e, masking_key_type const &key,
                size_t key_offset = 0);
void word_mask_exact (uint8_t *input, uint8_t *output, size_t length,
                      masking_key_type const &key);
void word_mask_exact (uint8_t *data, size_t length, masking_key_type const &
                      key);
size_t word_mask_circ (uint8_t *input, uint8_t *output, size_t length,
                       size_t prepared_key);
size_t word_mask_circ (uint8_t *data, size_t length, size_t prepared_key);

/// Check whether the frame's FIN bit is set.
/**
 * @param [in] h The basic header to extract from.
 * @return True if the header's fin bit is set.
 */
inline bool get_fin (basic_header const &h)
{
  return ( (h.b0 & BHB0_FIN) == BHB0_FIN);
}

/// Set the frame's FIN bit
/**
 * @param [out] h Header to set.
 * @param [in] value Value to set it to.
 */
inline void set_fin (basic_header &h, bool value)
{
  h.b0 = (value ? h.b0 | BHB0_FIN : h.b0 & ~BHB0_FIN);
}

/// check whether the frame's RSV1 bit is set
/**
 * @param [in] h The basic header to extract from.
 * @return True if the header's RSV1 bit is set.
 */
inline bool get_rsv1 (const basic_header &h)
{
  return ( (h.b0 & BHB0_RSV1) == BHB0_RSV1);
}

/// Set the frame's RSV1 bit
/**
 * @param [out] h Header to set.
 * @param [in] value Value to set it to.
 */
inline void set_rsv1 (basic_header &h, bool value)
{
  h.b0 = (value ? h.b0 | BHB0_RSV1 : h.b0 & ~BHB0_RSV1);
}

/// check whether the frame's RSV2 bit is set
/**
 * @param [in] h The basic header to extract from.
 * @return True if the header's RSV2 bit is set.
 */
inline bool get_rsv2 (const basic_header &h)
{
  return ( (h.b0 & BHB0_RSV2) == BHB0_RSV2);
}

/// Set the frame's RSV2 bit
/**
 * @param [out] h Header to set.
 * @param [in] value Value to set it to.
 */
inline void set_rsv2 (basic_header &h, bool value)
{
  h.b0 = (value ? h.b0 | BHB0_RSV2 : h.b0 & ~BHB0_RSV2);
}

/// check whether the frame's RSV3 bit is set
/**
 * @param [in] h The basic header to extract from.
 * @return True if the header's RSV3 bit is set.
 */
inline bool get_rsv3 (const basic_header &h)
{
  return ( (h.b0 & BHB0_RSV3) == BHB0_RSV3);
}

/// Set the frame's RSV3 bit
/**
 * @param [out] h Header to set.
 * @param [in] value Value to set it to.
 */
inline void set_rsv3 (basic_header &h, bool value)
{
  h.b0 = (value ? h.b0 | BHB0_RSV3 : h.b0 & ~BHB0_RSV3);
}

/// Extract opcode from basic header
/**
 * @param [in] h The basic header to extract from.
 * @return The opcode value of the header.
 */
inline opcode::value get_opcode (const basic_header &h)
{
  return opcode::value (h.b0 & BHB0_OPCODE);
}

/// check whether the frame is masked
/**
 * @param [in] h The basic header to extract from.
 * @return True if the header mask bit is set.
 */
inline bool get_masked (basic_header const &h)
{
  return ( (h.b1 & BHB1_MASK) == BHB1_MASK);
}

/// Set the frame's MASK bit
/**
 * @param [out] h Header to set.
 * @param value Value to set it to.
 */
inline void set_masked (basic_header &h, bool value)
{
  h.b1 = (value ? h.b1 | BHB1_MASK : h.b1 & ~BHB1_MASK);
}

/// Extracts the raw payload length specified in the basic header
/**
 * A basic WebSocket frame header contains a 7 bit value that represents the
 * payload size. There are two reserved values that are used to indicate that
 * the actual payload size will not fit in 7 bits and that the full payload
 * size is included in a separate field. The values are as follows:
 *
 * PAYLOAD_SIZE_CODE_16BIT (0x7E) indicates that the actual payload is less
 * than 16 bit
 *
 * PAYLOAD_SIZE_CODE_64BIT (0x7F) indicates that the actual payload is less
 * than 63 bit
 *
 * @param [in] h Basic header to read value from.
 * @return The exact size encoded in h.
 */
inline uint8_t get_basic_size (const basic_header &h)
{
  return h.b1 & BHB1_PAYLOAD;
}

/// Calculates the full length of the header based on the first bytes.
/**
 * A WebSocket frame header always has at least two bytes. Encoded within the
 * first two bytes is all the information necessary to calculate the full
 * (variable) header length. get_header_len() calculates the full header
 * length for the given two byte basic header.
 *
 * @param h Basic frame header to extract size from.
 * @return Full length of the extended header.
 */
inline size_t get_header_len (basic_header const &h)
{
  // TODO: check extensions?

  // masking key offset represents the space used for the extended length
  // fields
  size_t size = BASIC_HEADER_LENGTH + get_masking_key_offset (h);

  // If the header is masked there is a 4 byte masking key
  if (get_masked (h) ) {
    size += 4;
  }

  return size;
}

/// Calculate the offset location of the masking key within the extended header
/**
 * Calculate the offset location of the masking key within the extended header
 * using information from its corresponding basic header
 *
 * @param h Corresponding basic header to calculate from.
 *
 * @return byte offset of the first byte of the masking key
 */
inline unsigned int get_masking_key_offset (const basic_header &h)
{
  if (get_basic_size (h) == payload_size_code_16bit) {
    return 2;
  } else if (get_basic_size (h) == payload_size_code_64bit) {
    return 8;
  } else {
    return 0;
  }
}

/// Generate a properly sized contiguous string that encodes a full frame header
/**
 * Copy the basic header h and extended header e into a properly sized
 * contiguous frame header string for the purposes of writing out to the wire.
 *
 * @param h The basic header to include
 * @param e The extended header to include
 *
 * @return A contiguous string containing h and e
 */
inline std::string prepare_header (const basic_header &h, const
                                   extended_header &e)
{
  std::string ret;

  ret.push_back (char (h.b0) );
  ret.push_back (char (h.b1) );
  ret.append (
    reinterpret_cast<const char *> (e.bytes),
    get_header_len (h) - BASIC_HEADER_LENGTH
  );

  return ret;
}

/// Extract the masking key from a frame header
/**
 * Note that while read and written as an integer at times, this value is not
 * an integer and should never be interpreted as one. Big and little endian
 * machines will generate and store masking keys differently without issue as
 * long as the integer values remain irrelivant.
 *
 * @param h The basic header to extract from
 * @param e The extended header to extract from
 *
 * @return The masking key as an integer.
 */
inline masking_key_type get_masking_key (const basic_header &h, const
    extended_header &e)
{
  masking_key_type temp32;

  if (!get_masked (h) ) {
    temp32.i = 0;
  } else {
    unsigned int offset = get_masking_key_offset (h);
    std::copy (e.bytes + offset, e.bytes + offset + 4, temp32.c);
  }

  return temp32;
}

/// Extract the extended size field from an extended header
/**
 * It is the responsibility of the caller to verify that e is a valid extended
 * header. This function assumes that e contains an extended payload size.
 *
 * @param e The extended header to extract from
 *
 * @return The size encoded in the extended header in host byte order
 */
inline uint16_t get_extended_size (const extended_header &e)
{
  uint16_converter temp16;
  std::copy (e.bytes, e.bytes + 2, temp16.c);
  return ntohs (temp16.i);
}

/// Extract the jumbo size field from an extended header
/**
 * It is the responsibility of the caller to verify that e is a valid extended
 * header. This function assumes that e contains a jumbo payload size.
 *
 * @param e The extended header to extract from
 *
 * @return The size encoded in the extended header in host byte order
 */
inline uint64_t get_jumbo_size (const extended_header &e)
{
  uint64_converter temp64;
  std::copy (e.bytes, e.bytes + 8, temp64.c);
  return lib::net::_ntohll (temp64.i);
}

/// Extract the full payload size field from a WebSocket header
/**
 * It is the responsibility of the caller to verify that h and e together
 * represent a valid WebSocket frame header. This function assumes only that h
 * and e are valid. It uses information in the basic header to determine where
 * to look for the payload_size
 *
 * @param h The basic header to extract from
 * @param e The extended header to extract from
 *
 * @return The size encoded in the combined header in host byte order.
 */
inline uint64_t get_payload_size (const basic_header &h, const
                                  extended_header &e)
{
  uint8_t val = get_basic_size (h);

  if (val <= limits::payload_size_basic) {
    return val;
  } else if (val == payload_size_code_16bit) {
    return get_extended_size (e);
  } else {
    return get_jumbo_size (e);
  }
}

/// Extract a masking key into a value the size of a machine word.
/**
 * Machine word size must be 4 or 8.
 *
 * @param key Masking key to extract from
 *
 * @return prepared key as a machine word
 */
inline size_t prepare_masking_key (const masking_key_type &key)
{
  size_t low_bits = static_cast<size_t> (key.i);

  if (sizeof (size_t) == 8) {
    uint64_t high_bits = static_cast<size_t> (key.i);
    return static_cast<size_t> ( (high_bits << 32) | low_bits);
  } else {
    return low_bits;
  }
}

/// circularly shifts the supplied prepared masking key by offset bytes
/**
 * Prepared_key must be the output of prepare_masking_key with the associated
 * restrictions on the machine word size. offset must be greater than or equal
 * to zero and less than sizeof(size_t).
 */
inline size_t circshift_prepared_key (size_t prepared_key, size_t offset)
{
  if (lib::net::is_little_endian() ) {
    size_t temp = prepared_key << (sizeof (size_t) - offset) * 8;
    return (prepared_key >> offset * 8) | temp;
  } else {
    size_t temp = prepared_key >> (sizeof (size_t) - offset) * 8;
    return (prepared_key << offset * 8) | temp;
  }
}

/// Byte by byte mask/unmask
/**
 * Iterator based byte by byte masking and unmasking for WebSocket payloads.
 * Performs masking in place using the supplied key offset by the supplied
 * offset number of bytes.
 *
 * This function is simple and can be done in place on input with arbitrary
 * lengths and does not vary based on machine word size. It is slow.
 *
 * @param b Beginning iterator to start masking
 *
 * @param e Ending iterator to end masking
 *
 * @param o Beginning iterator to store masked results
 *
 * @param key 32 bit key to mask with.
 *
 * @param key_offset offset value to start masking at.
 */
template <typename input_iter, typename output_iter>
void byte_mask (input_iter first, input_iter last, output_iter result,
                masking_key_type const &key, size_t key_offset)
{
  size_t key_index = key_offset % 4;

  while (first != last) {
    *result = *first ^ key.c[key_index++];
    key_index %= 4;
    ++result;
    ++first;
  }
}

/// Byte by byte mask/unmask (in place)
/**
 * Iterator based byte by byte masking and unmasking for WebSocket payloads.
 * Performs masking in place using the supplied key offset by the supplied
 * offset number of bytes.
 *
 * This function is simple and can be done in place on input with arbitrary
 * lengths and does not vary based on machine word size. It is slow.
 *
 * @param b Beginning iterator to start masking
 *
 * @param e Ending iterator to end masking
 *
 * @param key 32 bit key to mask with.
 *
 * @param key_offset offset value to start masking at.
 */
template <typename iter_type>
void byte_mask (iter_type b, iter_type e, masking_key_type const &key,
                size_t key_offset)
{
  byte_mask (b, e, b, key, key_offset);
}

/// Exact word aligned mask/unmask
/**
 * Balanced combination of byte by byte and circular word by word masking.
 * Best used to mask complete messages at once. Has much higher setup costs than
 * word_mask_circ but works with exact sized buffers.
 *
 * Buffer based word by word masking and unmasking for WebSocket payloads.
 * Masking is done in word by word chunks with the remainder not divisible by
 * the word size done byte by byte.
 *
 * input and output must both be at least length bytes. Exactly length bytes
 * will be written.
 *
 * @param input buffer to mask or unmask
 *
 * @param output buffer to store the output. May be the same as input.
 *
 * @param length length of data buffer
 *
 * @param key Masking key to use
 */
inline void word_mask_exact (uint8_t *input, uint8_t *output, size_t length,
                             const masking_key_type &key)
{
  size_t prepared_key = prepare_masking_key (key);
  size_t n = length / sizeof (size_t);
  size_t *input_word = reinterpret_cast<size_t *> (input);
  size_t *output_word = reinterpret_cast<size_t *> (output);

  for (size_t i = 0; i < n; i++) {
    output_word[i] = input_word[i] ^ prepared_key;
  }

  for (size_t i = n * sizeof (size_t); i < length; i++) {
    output[i] = input[i] ^ key.c[i % 4];
  }
}

/// Exact word aligned mask/unmask (in place)
/**
 * In place version of word_mask_exact
 *
 * @see word_mask_exact
 *
 * @param data buffer to read and write from
 *
 * @param length length of data buffer
 *
 * @param key Masking key to use
 */
inline void word_mask_exact (uint8_t *data, size_t length, const
                             masking_key_type &key)
{
  word_mask_exact (data, data, length, key);
}

/// Circular word aligned mask/unmask
/**
 * Performs a circular mask/unmask in word sized chunks using pre-prepared keys
 * that store state between calls. Best for providing streaming masking or
 * unmasking of small chunks at a time of a larger message. Requires that the
 * underlying allocated size of the data buffer be a multiple of the word size.
 * Data in the buffer after `length` will be overwritten only with the same
 * values that were originally present.
 *
 * Buffer based word by word masking and unmasking for WebSocket payloads.
 * Performs masking in place using the supplied key. Casts the data buffer to
 * an array of size_t's and performs masking word by word. The underlying
 * buffer size must be a muliple of the word size.
 *
 * word_mask returns a copy of prepared_key circularly shifted based on the
 * length value. The returned value may be fed back into word_mask when more
 * data is available.
 *
 * input and output must both have length at least:
 *    ceil(length/sizeof(size_t))*sizeof(size_t)
 * Exactly that many bytes will be written, although only exactly length bytes
 * will be changed (trailing bytes will be replaced without masking)
 *
 * @param data Character buffer to mask
 *
 * @param length Length of data
 *
 * @param prepared_key Prepared key to use.
 *
 * @return the prepared_key shifted to account for the input length
 */
inline size_t word_mask_circ (uint8_t *input, uint8_t *output, size_t length,
                              size_t prepared_key)
{
  size_t n = length / sizeof (size_t); // whole words
  size_t l = length - (n * sizeof (size_t) ); // remaining bytes
  size_t *input_word = reinterpret_cast<size_t *> (input);
  size_t *output_word = reinterpret_cast<size_t *> (output);

  // mask word by word
  for (size_t i = 0; i < n; i++) {
    output_word[i] = input_word[i] ^ prepared_key;
  }

  // mask partial word at the end
  size_t start = length - l;
  uint8_t *byte_key = reinterpret_cast<uint8_t *> (&prepared_key);

  for (size_t i = 0; i < l; ++i) {
    output[start + i] = input[start + i] ^ byte_key[i];
  }

  return circshift_prepared_key (prepared_key, l);
}

/// Circular word aligned mask/unmask (in place)
/**
 * In place version of word_mask_circ
 *
 * @see word_mask_circ
 *
 * @param data Character buffer to read from and write to
 *
 * @param length Length of data
 *
 * @param prepared_key Prepared key to use.
 *
 * @return the prepared_key shifted to account for the input length
 */
inline size_t word_mask_circ (uint8_t *data, size_t length,
                              size_t prepared_key)
{
  return word_mask_circ (data, data, length, prepared_key);
}

/// Circular byte aligned mask/unmask
/**
 * Performs a circular mask/unmask in byte sized chunks using pre-prepared keys
 * that store state between calls. Best for providing streaming masking or
 * unmasking of small chunks at a time of a larger message. Requires that the
 * underlying allocated size of the data buffer be a multiple of the word size.
 * Data in the buffer after `length` will be overwritten only with the same
 * values that were originally present.
 *
 * word_mask returns a copy of prepared_key circularly shifted based on the
 * length value. The returned value may be fed back into byte_mask when more
 * data is available.
 *
 * @param data Character buffer to mask
 *
 * @param length Length of data
 *
 * @param prepared_key Prepared key to use.
 *
 * @return the prepared_key shifted to account for the input length
 */
inline size_t byte_mask_circ (uint8_t *input, uint8_t *output, size_t length,
                              size_t prepared_key)
{
  uint32_converter key;
  key.i = prepared_key;

  for (size_t i = 0; i < length; ++i) {
    output[i] = input[i] ^ key.c[i % 4];
  }

  return circshift_prepared_key (prepared_key, length % 4);
}

/// Circular byte aligned mask/unmask (in place)
/**
 * In place version of byte_mask_circ
 *
 * @see byte_mask_circ
 *
 * @param data Character buffer to read from and write to
 *
 * @param length Length of data
 *
 * @param prepared_key Prepared key to use.
 *
 * @return the prepared_key shifted to account for the input length
 */
inline size_t byte_mask_circ (uint8_t *data, size_t length,
                              size_t prepared_key)
{
  return byte_mask_circ (data, data, length, prepared_key);
}

} // namespace frame
} // namespace websocketpp

#endif //WEBSOCKETPP_FRAME_HPP
